/*
 * COPYRIGHT:       See COPYING in the top level directory
 * PROJECT:         Lite Bootsector for ISO file system
 * FILE:
 * PURPOSE:
 * PROGRAMMERS:     ?
 */

/* INCLUDES ******************************************************************/

#include <asm.inc>
#include "../freeldr/include/arch/pc/x86common.h"

.code16

// ****************************************************************************
//
//  isolinux.asm
//
//  A program to boot Linux kernels off a CD-ROM using the El Torito
//  boot standard in "no emulation" mode, making the entire filesystem
//  available.  It is based on the SYSLINUX boot loader for MS-DOS
//  floppies.
//
//   Copyright (C) 1994-2001  H. Peter Anvin
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
//  USA; either version 2 of the License, or (at your option) any later
//  version; incorporated herein by reference.
//
// ****************************************************************************
//
// THIS FILE IS A MODIFIED VERSION OF ISOLINUX.ASM
// MODIFICATION DONE BY MICHAEL K TER LOUW
// LAST UPDATED 3-9-2002
// SEE "COPYING" FOR INFORMATION ABOUT THE LICENSE THAT APPLIES TO THIS RELEASE
//
// ****************************************************************************
//
// This file is a modified version of ISOLINUX.ASM.
// Modification done by Eric Kohl
// Last update 04-25-2002
//
// ****************************************************************************

//#define DEBUG_MESSAGES                /* Uncomment to get debugging messages */
#ifndef ROS_REGTEST
#define WAIT_FOR_KEY
#endif

// ****************************************************************************
//    BEGIN THE BIOS/CODE/DATA SEGMENT
// ****************************************************************************
serial_base = HEX(0400)     // Base addresses for 4 serial ports (4 words)
BIOS_fbm = HEX(0413)        // Free Base Memory (kilobytes) (1 word)
BIOS_timer = HEX(046C)      // Timer ticks (1 word)
BIOS_magic = HEX(0472)      // BIOS reset magic (1 word)
BIOS_vidrows = HEX(0484)    // Number of screen rows (1 byte)

// Memory below this point is reserved for the BIOS and the MBR
trackbuf = HEX(1000)        // Track buffer goes here (8192 bytes)
trackbufsize = 8192         // trackbuf ends at 3000h

// struct open_file_t
file_sector = 0             // Sector pointer (0 = structure free)
file_left = 4               // Number of sectors left

//struct dir_t
dir_lba = 0                 // Directory start (LBA)
dir_len = 4                 // Length in bytes
dir_clust = 8               // Length in clusters

#define dir_t_size 12
#define open_file_t_size 8

MAX_OPEN_LG2 = 2            // log2(Max number of open files)
MAX_OPEN = 4
SECTORSIZE_LG2 = 11         // 2048 bytes/sector (El Torito requirement)
SECTORSIZE = 2048
retry_count    = 6          // How patient are we with the BIOS?

/******************************************************************************/
absolute HEX(5000)          // Here we keep our BSS stuff

resb DriveNo, 1             // CD-ROM BIOS drive number (BYTE)
resb DiskError, 1           // Error code for disk I/O (BYTE)
resb RetryCount, 1          // Used for disk access retries (BYTE)
resb TimeoutCount, 1        // Timeout counter (BYTE)
resb ISOFlags, 1            // Flags for ISO directory search (BYTE)
resb RootDir, dir_t_size    // Root directory (dir_t_size BYTES)
resb CurDir, dir_t_size     // Current directory (dir_t_size BYTES)
resb ISOFileName, 64        // ISO filename canonicalization buffer
resb ISOFileNameEnd, 1

//align open_file_t_size
absolute HEX(5060)
resb Files, (MAX_OPEN * open_file_t_size)


/******************************************************************************/


start:
    cli                     // Disable interrupts
    xor ax, ax              // ax = segment zero
    mov ss, ax              // Initialize stack segment
    mov sp, offset start    // Set up stack
    mov ds, ax              // Initialize other segment registers
    mov es, ax
    mov fs, ax
    mov gs, ax
    sti                     // Enable interrupts
    cld                     // Increment pointers

    mov cx, 2048 / 4        // Copy the bootsector
    mov si, HEX(7C00)       // from 0000:7C00
    mov di, HEX(7000)       // to 0000:7000
    rep movsd               // copy the program

    ljmp16 0, relocate      // jump into relocated code

relocate:
#ifdef DEBUG_MESSAGES
    // Display the banner and copyright
    mov si, offset isolinux_banner // si points to hello message
    call writestr            // display the message
    mov si, offset copyright_str
    call writestr
#endif

    // Make sure the keyboard buffer is empty
    call pollchar_and_empty

    // Check for MBR on harddisk
    pusha
    mov ax, HEX(0201)
    mov dx, HEX(0080)
    mov cx, HEX(0001)
    mov bx, trackbuf
    int HEX(13)
    popa
    jc .boot_cdrom          // could not read hdd

    push ax
#ifdef ROS_REGTEST // this change is taken from the original isobtrt.asm
    mov ax, word ptr ds:[trackbuf+510]
#else
    mov ax, word ptr ds:[trackbuf]
#endif
    cmp ax, 0
    je .boot_cdrom // no boot sector found (hopefully there are no weird bootsectors which begin with 0)
    pop ax

#ifdef WAIT_FOR_KEY
    // Display the 'Press key' message and wait for a maximum of 5 seconds
    call crlf
    mov si, offset presskey_msg // si points to 'Press key' message
    call writestr               // display the message

    mov byte ptr ds:[TimeoutCount], 5
.next_second:
    mov eax, ds:[BIOS_timer]        // load current tick counter
    add eax, 19

.poll_again:
    call pollchar_and_empty
    jnz .boot_cdrom

    mov ebx, ds:[BIOS_timer]
    cmp eax, ebx
    jnz .poll_again

    mov si, offset dot_msg            // print '.'
    call writestr
    dec byte ptr ds:[TimeoutCount]        // decrement timeout counter
    jz .boot_harddisk
    jmp .next_second
#endif

.boot_harddisk:
    call crlf

    // Boot first harddisk (drive 0x80)
    mov ax, HEX(0201)
    mov dx, HEX(0080)
    mov cx, HEX(0001)
    mov bx, HEX(7C00)
    int HEX(13)
    jnc .go_hd
    jmp kaboom
.go_hd:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov dx, HEX(0080)

    ljmp16 0, HEX(7C00)

.boot_cdrom:
#ifdef WAIT_FOR_KEY
    call crlf
    call crlf
#endif

    // Save and display the boot drive number
    mov byte ptr ds:[DriveNo], dl
#ifdef DEBUG_MESSAGES
    mov si, offset startup_msg
    call writemsg
    mov al, dl
    call writehex2
    call crlf
#endif

    // Now figure out what we're actually doing
    // Note: use passed-in DL value rather than 7Fh because
    // at least some BIOSes will get the wrong value otherwise
    mov ax, HEX(4B01)            // Get disk emulation status
    mov dl, byte ptr ds:[DriveNo]
    mov si, offset spec_packet
    int HEX(13)
    jc    spec_query_failed        // Shouldn't happen (BIOS bug)
    mov dl, byte ptr ds:[DriveNo]
    cmp byte ptr ds:[sp_drive], dl            // Should contain the drive number
    jne    spec_query_failed

#ifdef DEBUG_MESSAGES
    mov si, offset spec_ok_msg
    call writemsg
    mov al, byte ptr ds:[sp_drive]
    call writehex2
    call crlf
#endif

found_drive:
    // Get drive information
    mov ah, HEX(48)
    mov dl, byte ptr ds:[DriveNo]
    mov si, offset drive_params
    int HEX(13)
    jnc params_ok

    // mov si, nosecsize_msg    No use in reporting this
    // call writemsg

params_ok:
    // Check for the sector size (should be 2048, but
    // some BIOSes apparently think we're 512-byte media)
    //
    // FIX: We need to check what the proper behaviour
    // is for getlinsec when the BIOS thinks the sector
    // size is 512!!!  For that, we need such a BIOS, though...
#ifdef DEBUG_MESSAGES
    mov si, offset secsize_msg
    call writemsg
    mov ax, word ptr ds:[dp_secsize]
    call writehex4
    call crlf
#endif


    //
    // Clear Files structures
    //
    mov di, Files
    mov cx, (MAX_OPEN*open_file_t_size)/4
    xor    eax, eax
    rep    stosd

    //
    // Now, we need to sniff out the actual filesystem data structures.
    // mkisofs gave us a pointer to the primary volume descriptor
    // (which will be at 16 only for a single-session disk!); from the PVD
    // we should be able to find the rest of what we need to know.
    //
get_fs_structures:
    mov eax, 16            // Primary Volume Descriptor (sector 16)
    mov bx, trackbuf
    call getonesec

    mov eax, dword ptr ds:[trackbuf+156+2]
    mov dword ptr ds:[RootDir+dir_lba],eax
    mov dword ptr ds:[CurDir+dir_lba],eax
#ifdef DEBUG_MESSAGES
    mov si, offset rootloc_msg
    call writemsg
    call writehex8
    call crlf
#endif

    mov eax, dword ptr ds:[trackbuf+156+10]
    mov dword ptr ds:[RootDir+dir_len],eax
    mov dword ptr ds:[CurDir+dir_len],eax
#ifdef DEBUG_MESSAGES
    mov si, offset rootlen_msg
    call writemsg
    call writehex8
    call crlf
#endif
    add    eax,SECTORSIZE-1
    shr    eax,SECTORSIZE_LG2
    mov dword ptr ds:[RootDir+dir_clust],eax
    mov dword ptr ds:[CurDir+dir_clust],eax
#ifdef DEBUG_MESSAGES
    mov si, offset rootsect_msg
    call writemsg
    call writehex8
    call crlf
#endif

    // Look for the "REACTOS" directory, and if found,
    // make it the current directory instead of the root
    // directory.
    mov di, offset isolinux_dir
    mov al, 2                // Search for a directory
    call searchdir_iso
    jnz .dir_found
    mov si, offset no_dir_msg
    call writemsg
    jmp    kaboom

.dir_found:
    mov dword ptr ds:[CurDir+dir_len],eax
    mov eax, dword ptr ds:[si+file_left]
    mov dword ptr ds:[CurDir+dir_clust],eax
    xor    eax,eax                // Free this file pointer entry
    xchg    eax,dword ptr ds:[si+file_sector]
    mov dword ptr ds:[CurDir+dir_lba],eax


    mov di, offset isolinux_bin        // di points to Isolinux filename
    call searchdir            // look for the file
    jnz .isolinux_opened        // got the file
    mov si, offset no_isolinux_msg        // si points to error message
    call writemsg            // display the message
    jmp    kaboom                // fail boot

.isolinux_opened:
    mov di, si                // save file pointer

#ifdef DEBUG_MESSAGES
    mov si, offset filelen_msg
    call writemsg
    call writehex8
    call crlf
#endif

    mov ecx, eax            // calculate sector count
    shr ecx, 11
    test eax, HEX(7FF)
    jz .full_sector
    inc ecx
.full_sector:

#ifdef DEBUG_MESSAGES
    mov eax, ecx
    mov si, offset filesect_msg
    call writemsg
    call writehex8
    call crlf
#endif

// use high segment, as some bios can fail, when offset is too big
    mov bx, FREELDR_BASE / 16 // es = load segment
    mov es, bx
    xor ebx, ebx              // bx = load offset
    mov si, di                // restore file pointer
    mov cx, HEX(0FFFF)        // load the whole file
    call getfssec             // get the whole file

#ifdef DEBUG_MESSAGES
    mov si, offset startldr_msg
    call writemsg
    call crlf
#endif

    mov dl, byte ptr ds:[DriveNo]            // dl = boot drive
    mov dh, 0                    // dh = boot partition

    /* Transfer execution to the bootloader */
    ljmp16 0, FREELDR_BASE

//
// searchdir:
//
// Open a file
//
//  On entry:
//    DS:DI    = filename
//  If successful:
//    ZF clear
//    SI        = file pointer
//    DX:AX or EAX    = file length in bytes
//  If unsuccessful
//    ZF set
//

//
// searchdir_iso is a special entry point for ISOLINUX only.  In addition
// to the above, searchdir_iso passes a file flag mask in AL.  This is useful
// for searching for directories.
//
alloc_failure:
    xor    ax,ax                // ZF <- 1
    ret

searchdir:
    xor    al, al
searchdir_iso:
    mov byte ptr ds:[ISOFlags],al
    call allocate_file            // Temporary file structure for directory
    jnz alloc_failure
    push    es
    push    ds
    pop    es                // ES = DS
    mov si, offset CurDir
    cmp byte ptr ds:[di], 92 //'\'            // If filename begins with slash
    jne    .not_rooted
    inc    di                // Skip leading slash
    mov si, offset RootDir            // Reference root directory instead
.not_rooted:
    mov eax, dword ptr ds:[si+dir_clust]
    mov dword ptr ds:[bx+file_left],eax
    mov eax,dword ptr ds:[si+dir_lba]
    mov dword ptr ds:[bx+file_sector],eax
    mov edx,dword ptr ds:[si+dir_len]

.look_for_slash:
    mov ax,di
.scan:
    mov cl, byte ptr ds:[di]
    inc    di
    and    cl,cl
    jz    .isfile
    cmp cl, 92 // '\'
    jne    .scan
    mov byte ptr ds:[di-1], 0            // Terminate at directory name
    mov cl,2                // Search for directory
    xchg    cl, byte ptr ds:[ISOFlags]
    push    di
    push    cx
    push    offset .resume            // Where to "return" to
    push    es
.isfile:
    xchg    ax,di

.getsome:
    // Get a chunk of the directory
    mov si,trackbuf
    pushad
    xchg    bx,si
    mov cx,1                // load one sector
    call getfssec
    popad

.compare:
    movzx    eax, byte ptr ds:[si]            // Length of directory entry
    cmp al, 33
    jb    .next_sector
    mov cl, byte ptr ds:[si+25]
    xor    cl, byte ptr ds:[ISOFlags]
    test    cl, HEX(8E)            // Unwanted file attributes!
    jnz .not_file
    pusha
    movzx    cx, byte ptr ds:[si+32]        // File identifier length
    add    si, 33            // File identifier offset
    call iso_compare_names
    popa
    je    .success
.not_file:
    sub    edx, eax            // Decrease bytes left
    jbe    .failure
    add    si, ax                // Advance pointer

.check_overrun:
    // Did we finish the buffer?
    cmp si, trackbuf+trackbufsize
    jb    .compare            // No, keep going

    jmp    .getsome            // Get some more directory

.next_sector:
    // Advance to the beginning of next sector
    lea    ax, [si+SECTORSIZE-1]
    and    ax, not (SECTORSIZE-1)
    sub    ax, si
    jmp    .not_file            // We still need to do length checks

.failure:
#ifdef DEBUG_MESSAGES
    mov si, offset findfail_msg
    call writemsg
    call crlf
#endif
    xor    eax, eax            // ZF = 1
    mov dword ptr ds:[bx+file_sector], eax
    pop    es
    ret

.success:
    mov eax, dword ptr ds:[si+2]            // Location of extent
    mov dword ptr ds:[bx+file_sector], eax
    mov eax, dword ptr ds:[si+10]            // Data length
    push    eax
    add    eax, SECTORSIZE-1
    shr    eax, SECTORSIZE_LG2
    mov dword ptr ds:[bx+file_left], eax
    pop    eax
    mov edx, eax
    shr    edx, 16
    and    bx, bx                // ZF = 0
    mov si, bx
    pop    es
    ret

.resume:
    // We get here if we were only doing part of a lookup
    // This relies on the fact that .success returns bx == si
    xchg    edx, eax            // Directory length in edx
    pop    cx                // Old ISOFlags
    pop    di                // Next filename pointer

    // restore the backslash in the filename
    mov byte ptr ds:[di-1], 92 // '\'

    mov byte ptr ds:[ISOFlags], cl            // Restore the flags
    jz    .failure            // Did we fail?  If so fail for real!
    jmp    .look_for_slash            // Otherwise, next level

//
// allocate_file: Allocate a file structure
//
//        If successful:
//          ZF set
//          BX = file pointer
//        In unsuccessful:
//          ZF clear
//
allocate_file:
    push cx
    mov bx, Files
    mov cx, MAX_OPEN
.check:
    cmp dword ptr ds:[bx], 0
    je    .found
    add    bx, open_file_t_size        // ZF = 0
    loop    .check
    // ZF = 0 if we fell out of the loop
.found:
    pop    cx
    ret

//
// iso_compare_names:
//    Compare the names DS:SI and DS:DI and report if they are
//    equal from an ISO 9660 perspective.  SI is the name from
//    the filesystem; CX indicates its length, and ';' terminates.
//    DI is expected to end with a null.
//
//    Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
//
iso_compare_names:
    // First, terminate and canonicalize input filename
    push    di
    mov di, offset ISOFileName
.canon_loop:
    jcxz    .canon_end
    lodsb
    dec    cx
    cmp al, ';'
    je    .canon_end
    and    al, al
    je    .canon_end
    stosb
    cmp di, offset ISOFileNameEnd-1        // Guard against buffer overrun
    jb    .canon_loop
.canon_end:
    cmp di, ISOFileName
    jbe    .canon_done
    cmp byte ptr ds:[di-1], '.'        // Remove terminal dots
    jne    .canon_done
    dec    di
    jmp    short .canon_end
.canon_done:
    mov byte ptr ds:[di], 0            // Null-terminate string
    pop    di
    mov si, ISOFileName
.compare2:
    lodsb
    mov ah, byte ptr ds:[di]
    inc    di
    and    ax, ax
    jz    .success2            // End of string for both
    and    al, al                // Is either one end of string?
    jz    .failure2            // If so, failure
    and    ah, ah
    jz    .failure2
    or    ax, HEX(2020)            // Convert to lower case
    cmp al, ah
    je    .compare2
.failure2:
    and    ax, ax                // ZF = 0 (at least one will be nonzero)
.success2:
    ret







//
// getfssec: Get multiple clusters from a file, given the file pointer.
//
//  On entry:
//    ES:BX    -> Buffer
//    SI    -> File pointer
//    CX    -> Cluster count; 0FFFFh = until end of file
//  On exit:
//    SI    -> File pointer (or 0 on EOF)
//    CF = 1    -> Hit EOF
//
getfssec:
    cmp cx, word ptr ds:[si+file_left]
    jna    .ok_size
    mov cx, word ptr ds:[si+file_left]

.ok_size:
    mov bp, cx
    push    cx
    push    si
    mov eax, dword ptr ds:[si+file_sector]
    call getlinsec
    xor    ecx, ecx
    pop    si
    pop    cx

    add    dword ptr ds:[si+file_sector], ecx
    sub    dword ptr ds:[si+file_left], ecx
    ja    .not_eof            // CF = 0

    xor    ecx, ecx
    mov dword ptr ds:[si+file_sector], ecx        // Mark as unused
    xor    si,si
    stc

.not_eof:
    ret


// INT 13h, AX=4B01h, DL=<passed in value> failed.
// Try to scan the entire 80h-FFh from the end.
spec_query_failed:
    mov si, offset spec_err_msg
    call writemsg

    mov dl, HEX(0FF)
.test_loop:
    pusha
    mov ax, HEX(4B01)
    mov si, offset spec_packet
    mov byte ptr ds:[si], 13            // Size of buffer
    int HEX(13)
    popa
    jc    .still_broken

    mov si, offset maybe_msg
    call writemsg
    mov al, dl
    call writehex2
    call crlf

    cmp byte ptr ds:[sp_drive], dl
    jne    .maybe_broken

    // Okay, good enough...
    mov si, offset alright_msg
    call writemsg
    mov byte ptr ds:[DriveNo], dl
.found_drive:
    jmp    found_drive

    // Award BIOS 4.51 apparently passes garbage in sp_drive,
    // but if this was the drive number originally passed in
    // DL then consider it "good enough"
.maybe_broken:
    cmp byte ptr ds:[DriveNo], dl
    je .found_drive

.still_broken:
    dec dx
    cmp dl, HEX(80)
    jnb    .test_loop

fatal_error:
    mov si, offset nothing_msg
    call writemsg

.norge:
    jmp    .norge



// Information message (DS:SI) output
// Prefix with "isolinux: "
writemsg:
    push    ax
    push    si
    mov si, offset isolinux_str
    call writestr
    pop    si
    call writestr
    pop    ax
    ret

//
// crlf: Print a newline
crlf:
    mov si, offset crlf_msg
    // Fall through

//
// writestr: write a null-terminated string to the console, saving
//           registers on entry.
//
writestr:
    pushfd
    pushad
writestr_top:
    lodsb
    and    al, al
    jz    writestr_end
    call writechr
    jmp    short writestr_top
writestr_end:
    popad
    popfd
    ret

//
// writehex[248]: Write a hex number in (AL, AX, EAX) to the console
//
writehex2:
    pushfd
    pushad
    shl    eax, 24
    mov cx, 2
    jmp    short writehex_common
writehex4:
    pushfd
    pushad
    shl    eax, 16
    mov cx, 4
    jmp    short writehex_common
writehex8:
    pushfd
    pushad
    mov cx, 8
writehex_common:
.loop:
    rol    eax, 4
    push    eax
    and    al, HEX(0F)
    cmp al, 10
    jae    .high
.low:
    add    al, '0'
    jmp    short .ischar
.high:
    add    al, 'A'-10
.ischar:
    call writechr
    pop    eax
    loop    .loop
    popad
    popfd
    ret

//
// writechr: Write a character to the screen.  There is a more "sophisticated"
// version of this in the subsequent code, so we patch the pointer
// when appropriate.
writechr:
    pushfd
    pushad
    mov ah, HEX(0E)
    xor    bx, bx
    int HEX(10)
    popad
    popfd
    ret

//
// Get one sector.  Convenience entry point.
//
getonesec:
    mov bp, 1
    // Fall through to getlinsec

//
// Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
//
// Note that we can't always do this as a single request, because at least
// Phoenix BIOSes has a 127-sector limit.  To be on the safe side, stick
// to 32 sectors (64K) per request.
//
// Input:
//    EAX    - Linear sector number
//    ES:BX    - Target buffer
//    BP    - Sector count
//
getlinsec:
    push es // save es, we reset it later to 0

    mov si, offset dapa            // Load up the DAPA
    mov word ptr ds:[si+4], bx
    mov bx, es
    mov word ptr ds:[si+6], bx
    xor bx, bx // reset es to 0, some bioses (KVM) require that
    mov es, bx
    mov dword ptr ds:[si+8], eax
.loop2:
    push bp                // Sectors left
    cmp bp, word ptr ds:[MaxTransfer]
    jbe .bp_ok
    mov bp, word ptr ds:[MaxTransfer]
.bp_ok:
    mov word ptr ds:[si+2], bp
    push si
    mov dl, byte ptr ds:[DriveNo]
    mov ah, HEX(42)            // Extended Read
    call xint13
    pop si
    pop bp
    movzx eax,word ptr ds:[si+2]        // Sectors we read
    add dword ptr ds:[si+8], eax        // Advance sector pointer
    sub bp, ax            // Sectors left
    shl ax, SECTORSIZE_LG2-4        // 2048-byte sectors -> segment
    add word ptr ds:[si+6], ax            // Advance buffer pointer
    and bp, bp
    jnz .loop2
    mov eax, dword ptr ds:[si+8]            // Next sector

    pop es
    ret

    // INT 13h with retry
xint13:
    mov byte ptr ds:[RetryCount], retry_count
.try:
    pushad
    int HEX(13)
    jc    .error
    add    sp, 8*4            // Clean up stack
    ret
.error:
    mov byte ptr ds:[DiskError], ah        // Save error code
    popad
    dec byte ptr ds:[RetryCount]
    jz .real_error
    push ax
    mov al, byte ptr ds:[RetryCount]
    mov ah, byte ptr ds:[dapa+2]            // Sector transfer count
    cmp al,2            // Only 2 attempts left
    ja .nodanger
    mov ah,1            // Drop transfer size to 1
    jmp short .setsize
.nodanger:
    cmp al, retry_count-2
    ja .again            // First time, just try again
    shr ah,1            // Otherwise, try to reduce
    adc ah,0            // the max transfer size, but not to 0
.setsize:
    mov byte ptr ds:[MaxTransfer],ah
    mov byte ptr ds:[dapa+2],ah
.again:
    pop ax
    jmp .try

.real_error:
    mov si, offset diskerr_msg
    call writemsg
    mov al, byte ptr ds:[DiskError]
    call writehex2
    mov si, offset ondrive_str
    call writestr
    mov al, dl
    call writehex2
    call crlf
    // Fall through to kaboom

//
// kaboom: write a message and bail out.  Wait for a user keypress,
//      then do a hard reboot.
//
kaboom:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    sti
    mov si, offset err_bootfailed
    call writestr
    xor    ax, ax        // Wait for keypress
    int HEX(16)
    cli
    mov word ptr ds:[BIOS_magic], 0    // Cold reboot
    ljmp16 HEX(0F000), HEX(0FFF0)        // Reset vector address


//
// pollchar_and_empty: check if we have an input character pending (ZF = 0) and empty the input buffer afterwards
//
pollchar_and_empty:
    pushad
    mov ah, 1        // Did the user press a key?
    int HEX(16)
    jz    .end        // No, then we're done
    mov ah, 0        // Otherwise empty the buffer by reading it
    int HEX(16)
.end:
    popad
    ret


isolinux_banner:
    .ascii CR, LF, "Loading IsoBoot...", CR, LF, NUL
copyright_str:
    .ascii " (C) 1994-2002 H. Peter Anvin", CR, LF, NUL
presskey_msg:
    .ascii "Press any key to boot from CD", NUL
dot_msg:
    .ascii ".", NUL

#ifdef DEBUG_MESSAGES
startup_msg:
    .ascii "Startup, DL = '", NUL
spec_ok_msg:
    .ascii "packet OK, drive = ", NUL
secsize_msg:
    .ascii "size appears to be ", NUL
rootloc_msg:
    .ascii "Root dir loc: ", NUL
rootlen_msg:
    .ascii "Root dir len: ", NUL
rootsect_msg:
    .ascii "Root dir len(sect): ", NUL
fileloc_msg:
    .ascii "SETUPLDR loc: ", NUL
filelen_msg:
    .ascii "SETUPLDR len: ", NUL
filesect_msg:
    .ascii "SETUPLDR len(sect): ", NUL
findfail_msg:
    .ascii "Failed to find file!", NUL
startldr_msg:
    .ascii "Starting SETUPLDR.SYS", NUL
#endif

spec_err_msg:
    .ascii "Load spec failed, trying wing ...", CR, LF, NUL
maybe_msg:
    .ascii "Found smth at drive = ", NUL
alright_msg:
    .ascii "might be ok, continuing...", CR, LF, NUL
nothing_msg:
    .ascii "Failed locate CD-ROM; boot failed.", CR, LF, NUL

isolinux_str:
    .ascii "IsoBoot: ", NUL
crlf_msg:
    .ascii CR, LF, NUL
diskerr_msg:
    .ascii "Disk error ", NUL
ondrive_str:
    .ascii ", drive ", NUL

err_bootfailed:
    .ascii CR, LF, "failed..", NUL
isolinux_dir:
    .ascii "\\LOADER", NUL
no_dir_msg:
    .ascii "LOADER dir not found.", CR, LF, NUL
isolinux_bin:
    .ascii "SETUPLDR.SYS", NUL
no_isolinux_msg:
    .ascii "SETUPLDR not found.", CR, LF, NUL


//
// El Torito spec packet
//
.align 8
spec_packet:
    .byte HEX(13)          // Size of packet
sp_media:
    .byte 0                // Media type
sp_drive:
    .byte 0                // Drive number
sp_controller:
    .byte 0                // Controller index
sp_lba:
    .long 0                // LBA for emulated disk image
sp_devspec:
    .word 0                // IDE/SCSI information
sp_buffer:
    .word 0                // User-provided buffer
sp_loadseg:
    .word 0                // Load segment
sp_sectors:
    .word 0                // Sector count
sp_chs:
    .byte 0,0,0            // Simulated CHS geometry
sp_dummy:
    .byte 0                // Scratch, safe to overwrite

//
// EBIOS drive parameter packet
//
.align 8
drive_params:
    .word 30                // Buffer size
dp_flags:
    .word 0                // Information flags
dp_cyl:
    .long 0                // Physical cylinders
dp_head:
    .long 0                // Physical heads
dp_sec:
    .long 0                // Physical sectors/track
dp_totalsec:
    .long 0,0                // Total sectors
dp_secsize:
    .word 0                // Bytes per sector
dp_dpte:
    .long 0                // Device Parameter Table
dp_dpi_key:
    .word 0                // 0BEDDh if rest valid
dp_dpi_len:
    .byte 0                // DPI len
    .byte 0
    .word 0
dp_bus:
    .byte 0,0,0,0            // Host bus type
dp_interface:
    .byte 0,0,0,0,0,0,0,0            // Interface type
db_i_path:
    .long 0,0                // Interface path
db_d_path:
    .long 0,0                // Device path
    .byte 0
db_dpi_csum:
    .byte 0                // Checksum for DPI info

//
// EBIOS disk address packet
//
.align 8
dapa:
    .word 16                // Packet size
.count:
    .word 0                // Block count
.off:
    .word 0                // Offset of buffer
.seg:
    .word 0                // Segment of buffer
.lba:
    .long 0                // LBA (LSW)
    .long 0                // LBA (MSW)

.align 4
MaxTransfer:
    .word 2 //32                // Max sectors per transfer

.org 2046        // Pad to file offset 2046
.word HEX(0aa55)    // BootSector signature

.endcode16

END
